package xin.nick.common.filter;

import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import xin.nick.common.constant.SystemConstants;
import xin.nick.common.entity.LoginUser;
import xin.nick.common.util.ServletUtil;
import xin.nick.common.util.UserIdUtil;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * token过滤器 验证token有效性
 * 原本打算用jwt的,所以取了这个名字,
 * 实际上是用uuid+缓存 来实现的
 *
 * @author ruoyi
 */
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private RedisTemplate redisTemplate;

    public JwtAuthenticationTokenFilter() {}

    public JwtAuthenticationTokenFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        // 放行登录和验证码
        String requestURI = request.getRequestURI();
        if (SystemConstants.CODE_PATH.equals(requestURI) || SystemConstants.LOGIN_PATH.equals(requestURI)) {
            chain.doFilter(request, response);
            return;
        }

        LoginUser loginUser = null;
        String token = null;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (Objects.isNull(authentication)) {
            // 从缓存中取出用户信息
            token = ServletUtil.getToken(request);
            if (token != null) {
                String tokenKey = SystemConstants.USER_TOKEN_KEY + token;
                loginUser = (LoginUser) redisTemplate.opsForValue().get(tokenKey);

                if (Objects.nonNull(loginUser)) {
                    // 将用户信息放入 context
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }

            }
        } else {
            loginUser = (LoginUser) authentication.getPrincipal();
            // 当 authentication 为 null 时,应该会直接抛异常的
            // 但是这个防止出现自定义的 authentication 没抛异常而且是null
            // 所以加了一个判断
            if (Objects.nonNull(loginUser)) {
                token = loginUser.getToken();
            }

        }

        if (Objects.nonNull(loginUser)) {

            // 设置全局manager的 userID
            Long userId = loginUser.getUserId();
            UserIdUtil.setUserId(userId);

            // 重置用户过期时间
            String tokenKey = SystemConstants.USER_TOKEN_KEY + token;
            redisTemplate.expire(tokenKey, SystemConstants.USER_TOKEN_EXPIRE, TimeUnit.SECONDS);

        }
        chain.doFilter(request, response);
    }
}
